/****************************************************************************
 *
 * Copyright 2017 Samsung Electronics All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
 * either express or implied. See the License for the specific
 * language governing permissions and limitations under the License.
 *
 ****************************************************************************/
/****************************************************************************
 * arch/arm/src/tiva/tiva_adclib.c
 *
 *   Copyright (C) 2015 TRD2 Inc. All rights reserved.
 *   Author: Calvin Maguranis <calvin.maguranis@trd2inc.com>
 *
 * References:
 *
 *   TM4C123GH6PM Series Data Sheet
 *   TI Tivaware driverlib ADC sample code.
 *
 * The Tivaware sample code has a BSD compatible license that requires this
 * copyright notice:
 *
 * Copyright (c) 2005-2014 Texas Instruments Incorporated.  All rights reserved.
 * Software License Agreement
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *
 *   Redistributions of source code must retain the above copyright
 *   notice, this list of conditions and the following disclaimer.
 *
 *   Redistributions in binary form must reproduce the above copyright
 *   notice, this list of conditions and the following disclaimer in the
 *   documentation and/or other materials provided with the
 *   distribution.
 *
 *   Neither the name of Texas Instruments Incorporated nor the names of
 *   its contributors may be used to endorse or promote products derived
 *   from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This is part of revision 2.1.0.12573 of the Tiva Peripheral Driver Library.
 *****************************************************************************/

/****************************************************************************
 * Included Files
 ****************************************************************************/
#include <tinyara/arch.h>
#include <tinyara/analog/adc.h>

#include <sys/types.h>

#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>
#include <debug.h>
#include <assert.h>

#include <arch/board/board.h>

#include "up_arch.h"
#include "up_internal.h"
#include "tiva_gpio.h"
#include "tiva_adc.h"
#include "chip/tiva_adc.h"
#include "chip/tiva_pinmap.h"

#if defined(CONFIG_TIVA_ADC0) || defined(CONFIG_TIVA_ADC1)

/****************************************************************************
 * Pre-processor Definitions
 ****************************************************************************/

#define CLOCK_CONFIG(div, src) \
	(((((div) << ADC_CC_CLKDIV_SHIFT) & ADC_CC_CLKDIV_MASK) | \
	((src) & ADC_CC_CS_MASK)) & (ADC_CC_CLKDIV_MASK + ADC_CC_CS_MASK))

/****************************************************************************
 * Private Types
 ****************************************************************************/

/****************************************************************************
 * Private Function Prototypes
 ****************************************************************************/

/****************************************************************************
 * Private Data
 ****************************************************************************/

static uint8_t sse2irq[] = {
#ifdef CONFIG_TIVA_ADC0
	TIVA_IRQ_ADC0, TIVA_IRQ_ADC1, TIVA_IRQ_ADC2, TIVA_IRQ_ADC3
#endif
#ifdef CONFIG_TIVA_ADC1
	, TIVA_IRQ_ADC1_0, TIVA_IRQ_ADC1_1, TIVA_IRQ_ADC1_2, TIVA_IRQ_ADC1_3
#endif
};

static uint32_t ain2gpio[] = {
#ifdef GPIO_ADC_AIN0
	GPIO_ADC_AIN0
#endif
#ifdef GPIO_ADC_AIN1
	, GPIO_ADC_AIN1
#endif
#ifdef GPIO_ADC_AIN2
	, GPIO_ADC_AIN2
#endif
#ifdef GPIO_ADC_AIN3
	, GPIO_ADC_AIN3
#endif
#ifdef GPIO_ADC_AIN4
	, GPIO_ADC_AIN4
#endif
#ifdef GPIO_ADC_AIN5
	, GPIO_ADC_AIN5
#endif
#ifdef GPIO_ADC_AIN6
	, GPIO_ADC_AIN6
#endif
#ifdef GPIO_ADC_AIN7
	, GPIO_ADC_AIN7
#endif
#ifdef GPIO_ADC_AIN8
	, GPIO_ADC_AIN8
#endif
#ifdef GPIO_ADC_AIN9
	, GPIO_ADC_AIN9
#endif
#ifdef GPIO_ADC_AIN10
	, GPIO_ADC_AIN10
#endif
#ifdef GPIO_ADC_AIN11
	, GPIO_ADC_AIN11
#endif
#ifdef GPIO_ADC_AIN12
	, GPIO_ADC_AIN12
#endif
#ifdef GPIO_ADC_AIN13
	, GPIO_ADC_AIN13
#endif
#ifdef GPIO_ADC_AIN14
	, GPIO_ADC_AIN14
#endif
#ifdef GPIO_ADC_AIN15
	, GPIO_ADC_AIN15
#endif
#ifdef GPIO_ADC_AIN16
	, GPIO_ADC_AIN16
#endif
#ifdef GPIO_ADC_AIN17
	, GPIO_ADC_AIN17
#endif
#ifdef GPIO_ADC_AIN18
	, GPIO_ADC_AIN18
#endif
#ifdef GPIO_ADC_AIN19
	, GPIO_ADC_AIN19
#endif
#ifdef GPIO_ADC_AIN20
	, GPIO_ADC_AIN20
#endif
#ifdef GPIO_ADC_AIN21
	, GPIO_ADC_AIN21
#endif
#ifdef GPIO_ADC_AIN22
	, GPIO_ADC_AIN22
#endif
#ifdef GPIO_ADC_AIN23
	, GPIO_ADC_AIN23
#endif
};

/****************************************************************************
 * Public Data
 ****************************************************************************/

/****************************************************************************
 * Private Functions
 ****************************************************************************/

/****************************************************************************
 * Public Functions
 ****************************************************************************/

/****************************************************************************
 * Name: tiva_adc_one_time_init
 *
 * Description:
 *   Perform one-time initialization of global ADC settings; clock frequency
 *   and sampling rate.
 *
 * Assumptions/Limitations:
 *   Peripheral must be powered before one-time initialization.
 *
 ****************************************************************************/

void tiva_adc_one_time_init(uint32_t clock, uint8_t sample_rate)
{
	static bool one_time_init = false;

#ifdef CONFIG_DEBUG_ANALOG
	avdbg("setting clock=%d MHz sample rate=%d\n", clock, sample_rate);
#endif

	/* Have the common peripheral properties already been initialized? If yes,
	 * continue.
	 */

	if (one_time_init == false) {
		avdbg("performing ADC one-time initialization...\n");
		/* set clock */

		tiva_adc_clock(clock);

		/* set sampling rate */

		tiva_adc_sample_rate(sample_rate);

#ifdef CONFIG_ARCH_CHIP_TM4C129
		/* voltage reference */

		tiva_adc_vref();
#endif
		one_time_init = true;
	}
#ifdef CONFIG_DEBUG_ANALOG
	else {
		avdbg("one time initialization previously completed\n");
	}
#endif
}

/****************************************************************************
 * Name: tiva_adc_configure
 *
 * Description:
 *   Performs configuration of a single ADC, it's valid sample sequencers and
 *   available steps.
 *
 ****************************************************************************/

void tiva_adc_configure(struct tiva_adc_cfg_s *cfg)
{
	uint8_t s;
	uint8_t c;

	avdbg("configure ADC%d...\n", cfg->adc);

	/* Configure each SSE */

	for (s = 0; s < 4; ++s) {
		if (cfg->sse[s]) {
			tiva_adc_sse_cfg(cfg->adc, s, &cfg->ssecfg[s]);
		}
#ifdef CONFIG_DEBUG_ANALOG
		else {
			avdbg("ADC%d SSE%d has no configuration\n", cfg->adc, s);
		}
#endif
	}

	/* Configure each step */

	for (c = 0; c < cfg->steps; ++c) {
		tiva_adc_step_cfg(&cfg->stepcfg[c]);
	}

#ifdef CONFIG_DEBUG_ANALOG
	tiva_adc_dump_reg_cfg(cfg->adc, 0);
#endif
}

/****************************************************************************
 * Name: tiva_adc_sse_cfg
 *
 * Description:
 *   Configure the sample sequencer.
 *
 ****************************************************************************/

void tiva_adc_sse_cfg(uint8_t adc, uint8_t sse, struct tiva_adc_sse_cfg_s *ssecfg)
{
	avdbg("configure ADC%d SSE%d...\n", adc, sse);
#ifdef CONFIG_DEBUG_ANALOG
	avdbg("priority=%d trigger=%d...\n", ssecfg->priority, ssecfg->trigger);
#endif

	uint8_t priority = ssecfg->priority;
	uint8_t trigger = ssecfg->trigger;

	/* Ensure SSE is disabled before configuring */

	tiva_adc_sse_enable(adc, sse, false);

	/* Set conversion priority and trigger source for all steps in the SSE */

	tiva_adc_sse_priority(adc, sse, priority);
	tiva_adc_sse_trigger(adc, sse, trigger);
}

/****************************************************************************
 * Name: tiva_adc_step_cfg
 *
 * Description:
 *   Configures a sample sequencer step.
 *
 ****************************************************************************/

void tiva_adc_step_cfg(struct tiva_adc_step_cfg_s *stepcfg)
{
#ifdef CONFIG_DEBUG_ANALOG
	avdbg("  shold=0x%02x flags=0x%02x ain=%d...\n", stepcfg->shold, stepcfg->flags, stepcfg->ain);
#endif

	uint8_t adc = stepcfg->adc;
	uint8_t sse = stepcfg->sse;
	uint8_t step = stepcfg->step;
#ifdef CONFIG_EXPERIMENTAL
	uint8_t shold = stepcfg->shold;
#endif
	uint8_t flags = stepcfg->flags;
	uint8_t ain = stepcfg->ain;
	uint32_t gpio = ain2gpio[stepcfg->ain];

	avdbg("configure ADC%d SSE%d STEP%d...\n", adc, sse, step);

	/* Configure the AIN GPIO for analog input if not flagged to be muxed to
	 * the internal temperature sensor
	 */

	if ((flags & TIVA_ADC_FLAG_TS) != TIVA_ADC_FLAG_TS) {
		tiva_configgpio(gpio);
	}

	/* Register, set sample/hold time and configure the step */

	tiva_adc_sse_register_chn(adc, sse, step, ain);
	tiva_adc_sse_differential(adc, sse, step, 0);	/* TODO: update when differential
													 * support is added. */
#ifdef CONFIG_EXPERIMENTAL
	tiva_adc_sse_sample_hold_time(adc, sse, step, shold);
#endif
	tiva_adc_sse_step_cfg(adc, sse, step, flags);
}

/****************************************************************************
 * Name: tiva_adc_get_trigger
 *
 * Description:
 *   For a given adc, sse and step, get the AIN (channel) associated.
 *
 ****************************************************************************/

uint8_t tiva_adc_get_trigger(uint8_t adc, uint8_t sse)
{
	uintptr_t emuxaddr = TIVA_ADC_EMUX(adc);
	uint32_t emux = getreg32(emuxaddr) & ADC_EMUX_MASK(sse);
	return (emux >> ADC_EMUX_SHIFT(sse));
}

/****************************************************************************
 * Name: tiva_adc_get_ain
 *
 * Description:
 *   For a given adc, sse and step, get the AIN (channel) associated.
 *
 ****************************************************************************/

uint8_t tiva_adc_get_ain(uint8_t adc, uint8_t sse, uint8_t step)
{
	uintptr_t ssmuxaddr = TIVA_ADC_BASE(adc) + TIVA_ADC_SSMUX(sse);
	uint32_t ssmux = getreg32(ssmuxaddr) & ADC_SSMUX_MUX_MASK(step);
	return (ssmux >> ADC_SSMUX_MUX_SHIFT(step));
}

/* IRQS *********************************************************************/

/****************************************************************************
 * Name: tiva_adc_irq_attach
 *
 * Description:
 *  Attach a custom interrupt handler.
 *
 ****************************************************************************/

void tiva_adc_irq_attach(uint8_t adc, uint8_t sse, xcpt_t isr)
{

	uint32_t ret = 0;
	int irq = sse2irq[SSE_IDX(adc, sse)];

#ifdef CONFIG_DEBUG_ANALOG
	avdbg("assigning ISR=0x%p to ADC%d SSE%d IRQ=0x%02x...\n", isr, adc, sse, irq);
#endif

	ret = irq_attach(irq, isr, NULL);
	if (ret < 0) {
		adbg("ERROR: Failed to attach IRQ %d: %d\n", irq, ret);
		return;
	}

	up_enable_irq(irq);
}

/****************************************************************************
 * Name: tiva_adc_irq_detach
 *
 * Description:
 *  detach an interrupt handler.
 *
 ****************************************************************************/

void tiva_adc_irq_detach(uint8_t adc, uint8_t sse)
{
	uint32_t ret = 0;
	int irq = sse2irq[SSE_IDX(adc, sse)];

	/* Disable ADC interrupts at the level of the AIC */

	up_disable_irq(irq);

	/* Then detach the ADC interrupt handler. */

	ret = irq_detach(irq);
	if (ret < 0) {
		adbg("ERROR: Failed to detach IRQ %d: %d\n", irq, ret);
		return;
	}
}

/****************************************************************************
 * Name: tiva_adc_getirq
 *
 * Description:
 *  Maps ADC and SSE value to the correct IRQ value.
 *
 ****************************************************************************/

int tiva_adc_getirq(uint8_t adc, uint8_t sse)
{
	return sse2irq[SSE_IDX(adc, sse)];
}

/* Peripheral (base) level **************************************************/

/****************************************************************************
 * Name: tiva_adc_enable
 *
 * Description:
 *  Toggles the operational state of the ADC peripheral
 *
 * Input Parameters:
 *  state - operation state
 *
 ****************************************************************************/

int tiva_adc_enable(uint8_t adc, bool state)
{
	if (state == true) {
		/* Enable clocking to the ADC peripheral */

#ifdef TIVA_SYSCON_RCGCADC
		modifyreg32(TIVA_SYSCON_RCGCADC, 0, 1 << adc);
#else
		modifyreg32(TIVA_SYSCON_RCGC0, 0, SYSCON_RCGC0_ADC0);
#endif
		return OK;
	} else if (state == false) {
		/* Disable clocking to the ADC peripheral */

#ifdef TIVA_SYSCON_RCGCADC
		modifyreg32(TIVA_SYSCON_RCGCADC, 1 << adc, 0);
#else
		modifyreg32(TIVA_SYSCON_RCGC0, SYSCON_RCGC0_ADC0, 0);
#endif
		return OK;
	}

	/* ERROR! */

	return -1;
}

/****************************************************************************
 * Name: tiva_adc_clock
 *
 * Description:
 *  Sets the ADC peripherals clock to the desired frequency.
 *
 * Input Parameters:
 *  freq - ADC clock value; dependent on platform:
 *
 *  TM4C123 - Select either MOSC or PIOSC. Both result in 16 MHz operation,
 *  however the PIOSC allows the ADC to operate in deep sleep mode.
 *
 *  TM4C129 - For the 129, there is still a selection between various internal
 *  clocks, however the output frequency is variable (16 MHz - 32 MHz); so it
 *  is much more intuitive to allow the clock variable be a frequency value.
 *
 ****************************************************************************/

void tiva_adc_clock(uint32_t freq)
{
#if defined(CONFIG_ARCH_CHIP_TM4C123)
	/* For the TM4C123, the ADC clock source does not affect the frequency, it
	 * runs at 16 MHz regardless. You end up selecting between the MOSC (default)
	 * or the PIOSC. The PIOSC allows the ADC to operate even in deep sleep mode.
	 * Since this is the case, the clock value for the TM4C123 is always 16 MHz
	 */

	uintptr_t ccreg = (TIVA_ADC0_BASE + TIVA_ADC_CC_OFFSET);
	modifyreg32(ccreg, ADC_CC_CS_MASK, (ADC_CC_CS_PIOSC & ADC_CC_CS_MASK));

#elif defined(CONFIG_ARCH_CHIP_TM4C129)
	/* check clock bounds and specific match cases */

	uint32_t clk_src = 0;
	uint32_t div = 0;
	if (freq > TIVA_ADC_CLOCK_MAX) {
		clk_src = ADC_CC_CS_SYSPLL;
		div = (BOARD_FVCO_FREQUENCY / TIVA_ADC_CLOCK_MAX);
	} else if (freq < TIVA_ADC_CLOCK_MIN) {
		clk_src = ADC_CC_CS_PIOSC;
		div = 1;
	} else if (freq == XTAL_FREQUENCY) {
		clk_src = ADC_CC_CS_MOSC;
		div = 1;
	} else {
		clk_src = ADC_CC_CS_SYSPLL;
		div = (BOARD_FVCO_FREQUENCY / freq);
	}

	uintptr_t ccreg = (TIVA_ADC0_BASE + TIVA_ADC_CC_OFFSET);
	modifyreg32(ccreg, ADC_CC_CLKDIV_MASK, CLOCK_CONFIG(div, clk_src));
#else
#error Unsupported architecture reported
#endif
}

/****************************************************************************
 * Name: tiva_adc_vref
 *
 * Description:
 *  Sets the ADC peripherals clock to the desired frequency.
 *
 * Input Parameters:
 *  vref - ADC clock voltage reference source
 *
 ****************************************************************************/

#ifdef CONFIG_ARCH_CHIP_TM4C129
void tiva_adc_vref(uint8_t vref)
{
	uintptr_t ctlreg = (TIVA_ADC0_BASE + TIVA_ADC_CTL_OFFSET);
	modifyreg32(ctlreg, ADC_CTL_VREF_MASK, (vref & ADC_CTL_VREF_MASK));
}
#endif

/****************************************************************************
 * Name: tiva_adc_sample_rate
 *
 * Description:
 *  Sets the ADC sample rate as follows for each processor.
 *  TM4C123 - by maximum samples: 125 ksps, 250 ksps, 500 ksps or 1 Msps
 *  TM4C129 - by a divisor either being full, half, quarter or
 *  an eighth.
 *
 * Input Parameters:
 *  rate - ADC sample rate divisor
 *
 ****************************************************************************/

void tiva_adc_sample_rate(uint8_t rate)
{
	uintptr_t pcreg = (TIVA_ADC0_BASE + TIVA_ADC_PC_OFFSET);

	/* NOTE: ADC_PC_SR_MASK is intended for use with the TM4C123, the
	 * alternative is ADC_PC_MCR_MASK for the TM4C129. However both masks
	 * mask off the first 4 bits (0xF) so there is no need to distinguish
	 * between the two.
	 */

	modifyreg32(pcreg, ADC_PC_SR_MASK, (rate & ADC_PC_SR_MASK));
}

/****************************************************************************
 * Name: tiva_adc_proc_trig
 *
 * Description:
 *   Triggers the sample sequence to start it's conversion(s) and store them
 *   to the FIFO. This is only required when the trigger source is set to the
 *   processor.
 *
 * Input parameters:
 *   adc - which ADC peripherals' sample sequencers to trigger
 *   sse_mask - sample sequencer bitmask, each sse is 1 shifted by the sse
 *              number. e.g.
 *              SSE0 = 1 << 0
 *              SSE1 = 1 << 1
 *              SSE2 = 1 << 2
 *              SSE3 = 1 << 3
 *
 ****************************************************************************/

void tiva_adc_proc_trig(uint8_t adc, uint8_t sse_mask)
{
	uintptr_t pssireg = TIVA_ADC_PSSI(adc);
	putreg32((sse_mask & ADC_PSSI_TRIG_MASK), pssireg);
#ifdef CONFIG_TIVA_ADC_SYNC
#warning CONFIG_TIVA_ADC_SYNC unsupported at this time.
#endif
}

/****************************************************************************
 * Name: tiva_adc_int_status
 *
 * Description:
 *   Returns raw interrupt status for the input ADC
 *
 * Input parameters:
 *   adc - which ADC peripherals' interrupt status to retrieve
 *
 ****************************************************************************/

uint32_t tiva_adc_int_status(uint8_t adc)
{
	uint32_t ris = getreg32(TIVA_ADC_RIS(adc));
	return ris;
}

/* Sample sequencer (SSE) functions *****************************************/

/****************************************************************************
 * Name: tiva_adc_sse_enable
 *
 * Description:
 *   Sets the operation state of an ADC's sample sequencer (SSE). SSEs must
 *   be configured before being enabled.
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   state - sample sequencer enable/disable state
 *
 * Return value:
 *   Actual state of the ACTSS register.
 *
 ****************************************************************************/

uint8_t tiva_adc_sse_enable(uint8_t adc, uint8_t sse, bool state)
{
	avdbg("ADC%d SSE%d=%01d\n", adc, sse, state);

	uintptr_t actssreg = TIVA_ADC_ACTSS(adc);
	if (state == true) {
		modifyreg32(actssreg, 0, (1 << sse));
	} else {
		modifyreg32(actssreg, (1 << sse), 0);
	}

	return (getreg32(actssreg) & 0xF);
}

/****************************************************************************
 * Name: tiva_adc_sse_trigger
 *
 * Description:
 *   Sets the trigger configuration for an ADC's sample sequencer (SSE).
 *   Possible triggers are the following:
 *      - Processor
 *      - PWMs, requires that one of the PWMnn_TRIG_CFG defines be OR'd
 *        into the trigger value.
 *      - Timers
 *      - GPIO (which GPIO is platform specific, consult the datasheet)
 *      - Always
 *      - !!UNSUPPORTED: Comparators
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   trigger - interrupt trigger
 *
 ****************************************************************************/

void tiva_adc_sse_trigger(uint8_t adc, uint8_t sse, uint32_t trigger)
{
	uintptr_t emuxreg = (TIVA_ADC_EMUX(adc));
	uint32_t trig = ((trigger << ADC_EMUX_SHIFT(sse)) & ADC_EMUX_MASK(sse));
	modifyreg32(emuxreg, ADC_EMUX_MASK(sse), trig);

	/* NOTE: PWM triggering needs an additional register to be set (ADC_TSSEL)
	 * A platform specific IOCTL command is provided to configure the triggering.
	 */
}

/****************************************************************************
 * Name: tiva_adc_sse_pwm_trig
 *
 * Description:
 *   Additional triggering configuration for PWM. Sets which PWM and which
 *   generator.
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   cfg - which PWM modulator and generator to use, use TIVA_ADC_PWM_TRIG
 *         to encode the value correctly
 *
 ****************************************************************************/

#ifdef CONFIG_EXPERIMENTAL
void tiva_adc_sse_pwm_trig(uint8_t adc, uint8_t sse, uint32_t cfg)
{
	/* PWM triggering needs an additional register to be set (ADC_TSSEL) */

	uintptr_t tsselreg = TIVA_ADC_TSSEL(adc);

	modifyreg32(tsselreg, ADC_TSSEL_PS_MASK(see), cfg);
}
#endif

/****************************************************************************
 * Name: tiva_adc_sse_int_enable
 *
 * Description:
 *   Sets the interrupt state of an ADC's sample sequencer (SSE). SSEs must
 *   be enabled before setting interrupt state.
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   state - sample sequencer enable/disable interrupt state
 *
 ****************************************************************************/

void tiva_adc_sse_int_enable(uint8_t adc, uint8_t sse, bool state)
{
	irqstate_t flags;
	uintptr_t imreg = TIVA_ADC_IM(adc);
	int irq = tiva_adc_getirq(adc, sse);

	flags = irqsave();
	up_disable_irq(irq);

	tiva_adc_sse_clear_int(adc, sse);

	if (state == true) {
		modifyreg32(imreg, 0, (1 << sse));
	} else {
		modifyreg32(imreg, (1 << sse), 0);
	}

	up_enable_irq(irq);
	irqrestore(flags);
}

/****************************************************************************
 * Name: tiva_adc_sse_int_status
 *
 * Description:
 *   Returns interrupt status for the specificed SSE
 *
 * Input parameters:
 *   adc - which ADC peripherals' interrupt status to retrieve
 *   sse - which SSE interrupt status to retrieve
 *
 ****************************************************************************/

bool tiva_adc_sse_int_status(uint8_t adc, uint8_t sse)
{
	uint32_t intstat = tiva_adc_int_status(adc);
	uint32_t sseintstat = intstat & (1 << sse);
	return sseintstat > 0 ? true : false;
}

/****************************************************************************
 * Name: tiva_adc_sse_clear_int
 *
 * Description:
 *   Clears the interrupt bit for the SSE.
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   state - sample sequencer
 *
 ****************************************************************************/

void tiva_adc_sse_clear_int(uint8_t adc, uint8_t sse)
{
	uintptr_t iscreg = TIVA_ADC_ISC(adc);
	modifyreg32(iscreg, 0, (1 << sse));
}

/****************************************************************************
 * Name: tiva_adc_sse_data
 *
 * Description:
 *   Retrieves data from the FIFOs for all steps in the given sample sequencer.
 *   The input data buffer MUST be as large or larger than the sample sequencer.
 *   otherwise
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *
 * Return value:
 *   number of steps read from FIFO.
 *
 ****************************************************************************/

uint8_t tiva_adc_sse_data(uint8_t adc, uint8_t sse, int32_t *buf)
{
	uint32_t ssfstatreg = getreg32(TIVA_ADC_BASE(adc) + TIVA_ADC_SSFSTAT(sse));
	uint8_t fifo_count = 0;

	/* Read samples from the FIFO until it is empty */

	while (!(ssfstatreg & ADC_SSFSTAT_EMPTY) && fifo_count < 8) {
		/* Read the FIFO and copy it to the destination */

		buf[fifo_count] = getreg32(TIVA_ADC_BASE(adc) + TIVA_ADC_SSFIFO(sse));
		fifo_count++;

		/* refresh fifo status register state */

		ssfstatreg = getreg32(TIVA_ADC_BASE(adc) + TIVA_ADC_SSFSTAT(sse));
	}

	avdbg("fifo=%d\n", fifo_count);

	return fifo_count;
}

/****************************************************************************
 * Name: tiva_adc_sse_priority
 *
 * Description:
 *   Sets the priority configuration for an ADC's sample sequencer (SSE). The
 *   priority value ranges from 0 to 3, 0 being the highest priority, 3 being
 *   the lowest. There can be no duplicate values.
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   priority - conversion priority
 *
 ****************************************************************************/

void tiva_adc_sse_priority(uint8_t adc, uint8_t sse, uint8_t priority)
{
	uintptr_t ssprireg = TIVA_ADC_SSPRI(adc);
	uint32_t sspri = 0;

	sspri = (priority << ADC_SSPRI_SHIFT(sse)) & ADC_SSPRI_MASK(sse);
	modifyreg32(ssprireg, ADC_SSPRI_MASK(sse), sspri);
}

/****************************************************************************
 * Name: tiva_adc_sse_register_chn
 *
 * Description:
 *   Registers an input channel to an SSE. Channels are registered according
 *   to the step and channel values stored in the channel struct. If the SSE
 *   already has a channel registered, it is overwritten by the new channel.
 *
 *   *SSEMUX only supported on TM4C129 devices
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   chn - sample sequencer step
 *   ain - analog input pin
 *
 ****************************************************************************/

void tiva_adc_sse_register_chn(uint8_t adc, uint8_t sse, uint8_t chn, uint32_t ain)
{
	/* Configure SSE mux (SSMUX) with step number */

	uintptr_t ssmuxreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSMUX(sse));
	uint32_t step = 0;

	step = ((ain << ADC_SSMUX_MUX_SHIFT(chn)) & ADC_SSMUX_MUX_MASK(chn));
	modifyreg32(ssmuxreg, ADC_SSMUX_MUX_MASK(chn), step);

#ifdef CONFIG_ARCH_CHIP_TM4C129
	/* Configure SSE extended mux (SSEMUX) with step number and configuration */

	ssmuxreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSEMUX(sse));
	step = ((1 << ADC_SSEMUX_MUX_SHIFT(chn)) & ADC_SSEMUX_MUX_MASK(chn));
	modifyreg32(ssmuxreg, ADC_SSEMUX_MUX_MASK(chn), step);
#endif
}

/****************************************************************************
 * Name: tiva_adc_sse_differential
 *
 * Description:
 *   Sets the differential capability for a SSE. !! UNSUPPORTED
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   chn - sample sequencer channel
 *   diff - differential configuration
 *
 ****************************************************************************/

void tiva_adc_sse_differential(uint8_t adc, uint8_t sse, uint8_t chn, uint32_t diff)
{
#ifdef CONFIG_TIVA_ADC_DIFFERENTIAL
#error CONFIG_TIVA_ADC_DIFFERENTIAL unsupported!!
#else
	/* for now, ensure the FIFO is used and differential sampling is disabled */

	uintptr_t ssopreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSOP(sse));
	uint32_t sdcopcfg = (1 << chn);
	modifyreg32(ssopreg, sdcopcfg, 0);
#endif
}

/****************************************************************************
 * Name: tiva_adc_sse_sample_hold_time
 *
 * Description:
 *  Set the sample and hold time for this step.
 *
 *  This is not available on all devices, however on devices that do not
 *  support this feature these reserved bits are ignored on write access.
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   chn - sample sequencer channel
 *   shold - sample and hold time
 *
 ****************************************************************************/

#ifdef CONFIG_EXPERIMENTAL
void tiva_adc_sse_sample_hold_time(uint8_t adc, uint8_t sse, uint8_t chn, uint32_t shold)
{
	uintptr_t sstshreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSTSH(sse));
	modifyreg32(sstshreg, ADC_SSTSH_MASK(sse), (shold << ADC_SSTSH_SHIFT(sse)));
}
#endif

/****************************************************************************
 * Name: tiva_adc_sse_step_cfg
 *
 * Description:
 *   Configures the given SSE step to one of the following options:
 *      -Temperature sensor select: this step is muxed to the internal
 *       temperature sensor.
 *      -Interrupt enabled select: this step causes the interrupt bit to
 *       be set and, if the MASK0 bit in ADC_IM register is set, the
 *       interrupt is promoted to the interrupt controller.
 *      -Sequence end select: This step is the last sequence to be sampled.
 *       This MUST be set somewhere in the SSE.
 *      -*Comparator/Differential select: The analog input is differentially
 *       sampled. The corresponding ADCSSMUXn nibble must be set to the pair
 *       number "i", where the paired inputs are "2i and 2i+1". Because the
 *       temperature sensor does not have a differential option, this bit must
 *       not be set when the TS3 bit is set.
 *
 *  *Comparator/Differential functionality is unsupported and ignored.
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *   chn - sample sequencer channel
 *   cfg - step configuration
 *
 ****************************************************************************/

void tiva_adc_sse_step_cfg(uint8_t adc, uint8_t sse, uint8_t chn, uint8_t cfg)
{
	uintptr_t ssctlreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSCTL(sse));
	uint32_t ctlcfg = cfg << ADC_SSCTL_SHIFT(chn) & ADC_SSCTL_MASK(chn);
	modifyreg32(ssctlreg, ADC_SSCTL_MASK(chn), ctlcfg);
}

/****************************************************************************
 * Name: tiva_adc_dump_reg_cfg
 *
 * Description:
 *   Dump all configured registers for the given ADC and SSE. This should
 *   only be used to verify that configuration routines were accurate.
 *
 * Input parameters:
 *   adc - peripheral state
 *   sse - sample sequencer
 *
 ****************************************************************************/

#ifdef CONFIG_DEBUG_ANALOG
void tiva_adc_dump_reg_cfg(uint8_t adc, uint8_t sse)
{
	/* one-time initialization */

	uintptr_t ccreg = (TIVA_ADC0_BASE + TIVA_ADC_CC_OFFSET);	/* Clock */
	uintptr_t pcreg = (TIVA_ADC0_BASE + TIVA_ADC_PC_OFFSET);	/* Sample rate */

	/* SSE cfg */

	uintptr_t actssreg = TIVA_ADC_ACTSS(adc);	/* SSE enable state */
	uintptr_t ssprireg = TIVA_ADC_SSPRI(adc);	/* SSE priority */
	uintptr_t emuxreg = TIVA_ADC_EMUX(adc);	/* SSE trigger */

	/* step cfg */

	uintptr_t ssmuxreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSMUX(sse));	/* step registration */
#ifdef CONFIG_ARCH_CHIP_TM4C129
	uintptr_t ssemuxreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSEMUX(sse));	/* extended mux registration */
#endif
	uintptr_t ssopreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSOP(sse));	/* differential status */
#ifdef CONFIG_EXPERIMENTAL
	uintptr_t sstshreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSTSH(sse));	/* sample and hold time */
#endif
	uintptr_t ssctlreg = (TIVA_ADC_BASE(adc) + TIVA_ADC_SSCTL(sse));	/* step configuration */

	/* Get register contents */

	uint32_t cc = getreg32(ccreg);
	uint32_t pc = getreg32(pcreg);

	/* SSE cfg */

	uint32_t actss = getreg32(actssreg);
	uint32_t sspri = getreg32(ssprireg);
	uint32_t emux = getreg32(emuxreg);

	/* step cfg */

	uint32_t ssmux = getreg32(ssmuxreg);
#ifdef CONFIG_ARCH_CHIP_TM4C129
	uint32_t ssemux = getreg32(ssemuxreg);
#endif
	uint32_t ssop = getreg32(ssopreg);
#ifdef CONFIG_EXPERIMENTAL
	uint32_t sstsh = getreg32(sstshreg);
#endif
	uint32_t ssctl = getreg32(ssctlreg);

	/* Dump register contents */

	avdbg("CC     [0x%08x]=0x%08x\n", ccreg, cc);
	avdbg("PC     [0x%08x]=0x%08x\n", pcreg, pc);
	avdbg("ACTSS  [0x%08x]=0x%08x\n", actssreg, actss);
	avdbg("SSPRI  [0x%08x]=0x%08x\n", ssprireg, sspri);
	avdbg("EMUX   [0x%08x]=0x%08x\n", emuxreg, emux);
	avdbg("SSMUX  [0x%08x]=0x%08x\n", ssmuxreg, ssmux);
#ifdef CONFIG_ARCH_CHIP_TM4C129
	avdbg("SSEMUX [0x%08x]=0x%08x\n", ssemuxreg, ssemux);
#endif
	avdbg("SSOP   [0x%08x]=0x%08x\n", ssopreg, ssop);
#ifdef CONFIG_EXPERIMENTAL
	avdbg("SSTSH  [0x%08x]=0x%08x\n", sstshreg, sstsh);
#endif
	avdbg("SSCTL  [0x%08x]=0x%08x\n", ssctlreg, ssctl);

}
#endif							/* CONFIG_DEBUG_ANALOG */
#endif							/* CONFIG_TIVA_ADC0 || CONFIG_TIVA_ADC1 */
